package swust.fc.context;

import swust.fc.anno.*;
import swust.fc.bean.BeanDefinition;
import swust.fc.scanner.ClassPathScanner;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 简化版Spring容器实现，负责管理Bean的生命周期和依赖注入
 */
public class ApplicationContext {
    // 存储Bean定义
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    // 存储单例Bean实例
    private final Map<String, Object> singletonBeanMap = new HashMap<>();
    // 存储正在创建的Bean（用于解决循环依赖）
    private final Map<String, Object> beanMap = new HashMap<>();

    /**
     * 构造函数，初始化容器
     *
     * @param clazz 配置类
     */
    public ApplicationContext(Class<?> clazz) {
        try {
            //扫描组件并注册Bean定义
            scanComponents(clazz);
            //实例化所有单例Bean
            instanceSingleton();
        } catch (Exception e) {
            throw new RuntimeException("容器初始化失败", e);
        }
    }

    /**
     * 扫描组件并注册Bean定义
     *
     * @param clazz 配置类
     * @throws Exception 扫描过程中可能抛出的异常
     */
    private void scanComponents(Class<?> clazz) {
        // 获取@ComponentScan注解
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        if (componentScan == null) {
            throw new RuntimeException("配置类必须包含@ComponentScan注解");
        }
        // 获取要扫描的包路径
        String[] packages = componentScan.value();
        ClassPathScanner classPathScanner = new ClassPathScanner();
        classPathScanner.scan(packages);

        // 处理组件类（带有@Component注解的类）
        for (Class<?> cs : classPathScanner.getClasses()) {
            if (cs.isAnnotationPresent(Component.class)) {
                Component component = cs.getAnnotation(Component.class);
                //获取Bean名称，如果未指定则使用默认名称
                String beanName = component.value().isEmpty() ? getDefaultBeanName(cs) : component.value();
                // 创建Bean定义并注册
                BeanDefinition beanDefinition = new BeanDefinition(beanName, cs);
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        }
        // 处理配置类中的@Bean方法
        if (clazz.isAnnotationPresent(Configuration.class)) {
            //遍历类中的所有方法
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(Bean.class)) {
                    Bean bean = method.getAnnotation(Bean.class);
                    // 获取Bean名称，如果未指定则使用方法名
                    String beanName = bean.value().isEmpty() ? method.getName() : bean.value();
                    //创建Bean定义并设置工厂方法
                    BeanDefinition beanDefinition = new BeanDefinition(beanName, method.getReturnType());
                    beanDefinition.setFactoryMethod(method);
                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            }
        }

    }

    /**
     * 实例化所有单例Bean
     *
     * @throws Exception 实例化过程中可能抛出的异常
     */
    private void instanceSingleton() throws Exception {
        for (String beanName : beanMap.keySet()) {
            getBean(beanName);
        }
    }

    /**
     * 获取Bean实例
     *
     * @param beanName Bean名称
     * @return Bean实例
     * @throws Exception 获取Bean过程中可能抛出的异常
     */
    private Object getBean(String beanName) throws Exception {
        //先从实例池中获取
        Object bean = singletonBeanMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        //检查是否正在创建中（解决循环依赖）
        bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        //如果没有，则创建新的实例
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new RuntimeException("未找到Bean定义: " + beanName);
        }

        //实例化bean
        bean = createBean(beanName, beanDefinition);
        if ("singleton".equals(beanDefinition.getScope())) {//如果是单例的，放入单例队列，从创建队列移除
            singletonBeanMap.put(beanName, bean);
            beanMap.remove(beanName);
        }
        return bean;

    }

    /**
     * 根据类型获取Bean
     *
     * @param <T>   Bean类型
     * @param clazz Bean类型
     * @return Bean实例
     */
    public <T> T getBean(Class<T> clazz) throws Exception {
        for (BeanDefinition beanDefinition : beanDefinitionMap.values()) {
            if (clazz.isAssignableFrom(beanDefinition.getBeanClass())) {
                return (T) getBean(beanDefinition.getBeanName());
            }
        }
        throw new RuntimeException("未找到类型为 " + clazz.getName() + " 的Bean");
    }

    /**
     * 创建Bean实例
     *
     * @param beanName       Bean名称
     * @param beanDefinition Bean定义
     * @return Bean实例
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) throws Exception {
        Object instance = null;
        Method factoryMethod = beanDefinition.getFactoryMethod();
        if (factoryMethod != null) {//工厂方法不为null，那么用指定的方法创建实例
            // 创建配置类实例
            Object configInstance = factoryMethod.getDeclaringClass().newInstance();
            // 调用工厂方法
            instance = factoryMethod.invoke(configInstance);
        } else {//否则用构造方法
            instance = beanDefinition.getBeanClass().newInstance();
        }
        //将早期引用放入缓存（解决循环依赖）
        beanMap.put(beanName, instance);
        //依赖注入
        injectDependencies(instance);
        return instance;

    }

    /**
     * 依赖注入
     *
     * @param instance Bean实例
     * @throws Exception 依赖注入过程中可能抛出的异常
     */
    private void injectDependencies(Object instance) throws Exception {
        Class<?> clazz = instance.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {//如果字段上面有@Autowired注解，进行依赖注入
                // 获取字段类型
                Class<?> type = field.getType();
                Object dependency = getBean(type);
                field.setAccessible(true);
                field.set(instance, dependency);
            }
        }
    }

    /**
     * 获取默认的Bean名称（类名首字母小写）
     *
     * @param clazz 类对象
     * @return 默认Bean名称
     */
    private String getDefaultBeanName(Class<?> clazz) {
        String className = clazz.getSimpleName();
        return className.substring(0, 1).toLowerCase() + className.substring(1);
    }


}
